package com.shxzz.easy12306.services.pay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.shxzz.easy12306.framework.starter.common.mq.event.PayResultCallbackOrderEvent;
import com.shxzz.easy12306.framework.starter.common.mq.event.RefundResultCallbackOrderEvent;
import com.shxzz.easy12306.framework.starter.convention.exceptions.ServiceException;
import com.shxzz.easy12306.framework.starter.designpattern.strategy.AbstractStrategyChoose;
import com.shxzz.easy12306.services.pay.common.enums.TradeStatusEnum;
import com.shxzz.easy12306.services.pay.dao.entity.PayDO;
import com.shxzz.easy12306.services.pay.dao.mapper.PayMapper;
import com.shxzz.easy12306.services.pay.dto.base.PayRequest;
import com.shxzz.easy12306.services.pay.dto.base.RefundRequest;
import com.shxzz.easy12306.services.pay.dto.convert.RefundCommandConvert;
import com.shxzz.easy12306.services.pay.dto.domain.PayCallbackDTO;
import com.shxzz.easy12306.framework.starter.common.dto.pay.RefundReqDTO;
import com.shxzz.easy12306.services.pay.dto.resp.PayResponse;
import com.shxzz.easy12306.services.pay.dto.resp.RefundResponse;
import com.shxzz.easy12306.services.pay.mq.producer.PayCallbackOrderProducer;
import com.shxzz.easy12306.services.pay.mq.producer.RefundCallbackOrderProducer;
import com.shxzz.easy12306.services.pay.service.PayService;
import com.shxzz.easy12306.services.pay.service.payid.PayIdGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;

/**
 * 支付服务层实现
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PayServiceImpl implements PayService {

    private final AbstractStrategyChoose abstractStrategyChoose;
    private final PayIdGenerator payIdGenerator;

    private final PayMapper payMapper;

    private final PayCallbackOrderProducer payCallbackOrderProducer;
    private final RefundCallbackOrderProducer refundCallbackOrderProducer;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public PayResponse commonPay(PayRequest payRequest) {
        LambdaQueryWrapper<PayDO> queryWrapper = Wrappers.lambdaQuery(PayDO.class)
                .eq(PayDO::getOrderSn, payRequest.getOrderSn());
        List<PayDO> payDOList = payMapper.selectList(queryWrapper);
        if(CollUtil.isNotEmpty(payDOList)) {
            log.info("用户再次发起支付");
            payMapper.deleteBatchIds(payDOList);
        }
        PayDO payDO = BeanUtil.toBean(payRequest, PayDO.class);
        String paySn = payIdGenerator.getPayId(payRequest.getOrderSn());
        payDO.setPaySn(paySn);
        payDO.setStatus(TradeStatusEnum.WAIT_BUYER_PAY.getTradeCode());
        payDO.setTotalAmount(payRequest.getTotalAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).intValue());
        int insert = payMapper.insert(payDO);
        if (insert <= 0) {
            log.error("支付单创建失败，支付聚合根：{}", JSON.toJSONString(payRequest));
            throw new ServiceException("支付单创建失败");
        }
        return abstractStrategyChoose.chooseAndExecuteResp(payRequest.buildMark(), payRequest);
    }

    @Override
    public void callbackPay(PayCallbackDTO payCallbackRequestParam) {
        LambdaQueryWrapper<PayDO> queryWrapper = Wrappers.lambdaQuery(PayDO.class)
                .eq(PayDO::getOrderSn, payCallbackRequestParam.getOrderSn());
        PayDO payDO = payMapper.selectOne(queryWrapper);
        if (Objects.isNull(payDO)) {
            log.error("支付单不存在，orderRequestId：{}", payCallbackRequestParam.getOrderRequestId());
            throw new ServiceException("支付单不存在");
        }
        payDO.setTradeNo(payCallbackRequestParam.getTradeNo());
        payDO.setStatus(payCallbackRequestParam.getStatus());
        payDO.setPayAmount(payCallbackRequestParam.getPayAmount());
        payDO.setGmtPayment(payCallbackRequestParam.getGmtPayment());
        LambdaUpdateWrapper<PayDO> updateWrapper = Wrappers.lambdaUpdate(PayDO.class)
                .eq(PayDO::getOrderSn, payCallbackRequestParam.getOrderSn());
        int result = payMapper.update(payDO, updateWrapper);
        if (result <= 0) {
            log.error("修改支付单支付结果失败，支付单信息：{}", JSON.toJSONString(payDO));
            throw new ServiceException("修改支付单支付结果失败");
        }
        // 交易成功，回调订单服务告知支付结果，修改订单流转状态
        if (Objects.equals(payCallbackRequestParam.getStatus(), TradeStatusEnum.TRADE_SUCCESS.getTradeCode())) {
            payCallbackOrderProducer.sendMessage(BeanUtil.toBean(payDO, PayResultCallbackOrderEvent.class));
        }
    }

    @Override
    public Void commonRefund(RefundReqDTO requestParam) {
        LambdaQueryWrapper<PayDO> queryWrapper = Wrappers.lambdaQuery(PayDO.class)
                .eq(PayDO::getOrderSn, requestParam.getOrderSn());
        PayDO payDO = payMapper.selectOne(queryWrapper);
        RefundRequest refundRequest = RefundCommandConvert.convert(payDO, requestParam);
        RefundResponse refundResponse = abstractStrategyChoose.chooseAndExecuteResp(refundRequest.buildMark(), refundRequest);
        PayDO updatePayDO = new PayDO();
        updatePayDO.setId(payDO.getId());
        updatePayDO.setPayAmount(payDO.getPayAmount() - requestParam.getRefundAmount());
        updatePayDO.setStatus(refundResponse.getStatus());
        payMapper.updateById(updatePayDO);

        if (Objects.equals(refundResponse.getStatus(), TradeStatusEnum.TRADE_CLOSED.getTradeCode())) {
            RefundResultCallbackOrderEvent refundResultCallbackOrderEvent = RefundResultCallbackOrderEvent.builder()
                    .orderSn(requestParam.getOrderSn())
                    .refundType(requestParam.getRefundType())
                    .refundPassengerDetailList(requestParam.getRefundDetailReqDTOList())
                    .build();
            refundCallbackOrderProducer.sendMessage(refundResultCallbackOrderEvent);
        }
        return null;
    }
}




